package select_columns

import (
	"fmt"

	"github.com/example-user/go-pocket-etl/pkg/core"
	"github.com/example-user/go-pocket-etl/pkg/factory"
)

// init 实现了组件的自注册。
func init() {
	factory.RegisterProcessor("select_columns", NewProcessor)
}

// Processor 实现了 core.Processor 接口，用于从每条记录中筛选出指定的列，并丢弃所有其他列。
// 这是一个典型的“白名单”模式，常用于减少数据体积、移除不必要的字段，或在数据发送到外部系统前进行清理。
type Processor struct {
	columnsToKeep []string // 需要保留的列名列表。
}

// NewProcessor 是 select_columns.Processor 的构造函数，由工厂调用。
func NewProcessor() core.Processor {
	return &Processor{}
}

// Open 从配置中解析需要保留的列名列表。
//
// 它期望配置中有一个名为 `columns` 的键，其值为一个字符串数组。
// YAML 配置示例:
//   - type: select_columns
//     params:
//     columns:
//   - "user_id"
//   - "email"
//   - "created_at"
func (p *Processor) Open(config map[string]interface{}) error {
	columnsVal, ok := config["columns"]
	if !ok {
		return fmt.Errorf("select_columns processor: config is missing required key 'columns'")
	}

	// YAML 解析器会将字符串数组解析为 []interface{}，这里需要进行类型断言和转换。
	columns, ok := columnsVal.([]interface{})
	if !ok {
		return fmt.Errorf("select_columns processor: 'columns' must be an array of strings")
	}

	if len(columns) == 0 {
		return fmt.Errorf("select_columns processor: 'columns' array cannot be empty")
	}

	p.columnsToKeep = make([]string, len(columns))
	for i, c := range columns {
		colStr, ok := c.(string)
		if !ok {
			return fmt.Errorf("select_columns processor: 'columns' array must contain only strings, but found a non-string element")
		}
		p.columnsToKeep[i] = colStr
	}

	return nil
}

// Process 方法根据 `columnsToKeep` 列表对输入的记录进行转换，生成一个只包含指定列的新记录。
//
// 一个关键的设计决策是：如果在输入记录中找不到配置中指定的某个列名，它会被静默忽略，而不会报错。
// 这使得管道对上游数据模式的微小变化（例如，某列被移除）更具弹性。
func (p *Processor) Process(record core.Record) (core.Record, error) {
	newRecord := make(core.Record, len(p.columnsToKeep))

	for _, colName := range p.columnsToKeep {
		// 检查原始记录中是否存在该列。
		if val, ok := record[colName]; ok {
			// 如果存在，则将其添加到新记录中。
			newRecord[colName] = val
		}
	}

	return newRecord, nil
}

// Close 是一个无操作（no-op）方法，因为 select_columns 处理器是无状态的，不需要在处理结束后清理任何资源。
func (p *Processor) Close() error {
	return nil
}
